/* $Id: buffer.cc jjs $ */
/*
   Copyright (C) 2007 by Jurjen Stellingwerff <jurjen@stwerff.xs4all.nl>
   Part of the Moros Project http://moros.sourceforge.net/

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License version 2 or any
   later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY.

   See the COPYING file for more details.
*/

#define DEBUG_ROUTINE "buffer"

#include <iostream>
#include "buffer.h"
#include "log.h"
#include "errno.h"

/// Number of byes to allocate at a minimum when a buffer is too small
#define BUFFER_INCREASE 100

buffer::buffer(void) {
	size_ = BUFFER_INCREASE;
	data_ = (char*)malloc(size_);
	read_pos_ = 0;
	write_pos_ = 0;
	last_pos_ = 0;
	//std::cout << "Creating buffer\n";
}

buffer::~buffer(void) {
	free(data_);
	//std::cout << "Destroying buffer\n";
}

void buffer::write(const std::string data) {
	int len=data.length();
	memcpy(writing(len), data.c_str(), len);
	write_pos_ += len;
}

int buffer::fwrite(int fd) {
    int len=read(fd, writing(4096), 4096);
    if (len>0) {
        write_pos_ += len;
        return 0;
    }
    if (errno==EAGAIN) return 0;
    return 1; // ready with file or error
}

char* buffer::send(long *length) {
	char* res = data_ + last_pos_;
	if (length != NULL) {
		*length = write_pos_ - last_pos_;
		//std::cout << "Send length=" << *length << " position " << write_pos_ << "\n";
	} else {
		//std::cout << "Finished writing position " << write_pos_ << "\n";
	}
	last_pos_ = write_pos_;
	return res;
}

char* buffer::writing(int bytes)
{
	if (write_pos_ + bytes > size_) {
		//std::cout << "Old length " << size_ << "\n";
		size_ = write_pos_ + bytes + BUFFER_INCREASE;
		//std::cout << "New length " << size_ << "\n";
		data_ = (char*)realloc(data_, size_);
	}
	return data_ + write_pos_;;
}

void buffer::wrote(int bytes)
{
	//std::cout << "wrote (" << write_pos_ << "." << bytes << "): " << debug_(write_pos_, bytes) << "\n";
	write_pos_ += bytes;
}

unsigned long buffer::bytes(void)
{
	return last_pos_ - read_pos_;
}

char *buffer::ptr(void)
{
	return data_ + read_pos_;
}

std::string buffer::debug_(int pos, int len) {
	char *s=(char*)malloc(len+1);
	if (s==NULL) {
		dbg_fatal("buffer could not claim memory");
	}
	memcpy(s, data_ + pos, len);
    s[len]=0;
    std::string res=s;
	free(s);
	return res;
}

void buffer::done(unsigned long bytes)
{
	//std::cout << "Done (" << read_pos_ << "," << bytes << "): '" << debug_(read_pos_, bytes) << "'\n";
	if (bytes > last_pos_ - read_pos_) {
		dbg_error("You are reading more than there is data in the buffer...");
		dbg_calls("read_pos: %i bytes: %i last_pos: %i write_pos: %i",
            read_pos_, bytes, last_pos_, write_pos_);
		bytes=last_pos_ - read_pos_;
	}
	read_pos_ += bytes;
	//std::cout << "Check " << read_pos_ << " vs " << (write_pos_ - read_pos_)*20 << "\n";
	if (read_pos_ > (write_pos_ - read_pos_)*20) {
		//std::cout << "Before move: read_pos: " << read_pos_ << " bytes: " << bytes << " last_pos: " << last_pos_ << " write_pos: " << write_pos_ << "\n";
		memmove(data_, data_ + read_pos_, write_pos_ - read_pos_);
		last_pos_ -= read_pos_;
		write_pos_ -= read_pos_;
		read_pos_ = 0;
		//std::cout << "After move: read_pos: " << read_pos_ << " bytes: " << bytes << " last_pos: " << last_pos_ << " write_pos: " << write_pos_ << "\n";
	}
}
